﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using RaisingForce.Raise.Measures;

namespace RaisingForce.Raise.Presentation.ViewModel.Specialized
{
    public static class MeasureHelper
    {
        #region Distance Measure Declarations

        public static readonly IDistanceMeasure DefaultDistanceMeasure =
            DistanceMeasures.Centimetre;

        public static IDistanceMeasure GetDistanceMeasure(string measureKey)
        {
            if (measureKey == null)
            {
                throw new ArgumentNullException("measureKey");
            }
            Contract.EndContractBlock();
            return distanceMeasures.GetValueOrDefault(measureKey);
        }

        public static string GetDistanceMeasureKey(IDistanceMeasure measure)
        {
            if (measure == null)
            {
                throw new ArgumentNullException("measure");
            }
            Contract.EndContractBlock();
            return distanceMeasures.GetKeyOrDefault(measure);
        }

        public static IEnumerable<IDistanceMeasure> SupportedDistanceMeasures
        {
            get { return distanceMeasures.GetReadOnlyValues(); }
        }

        public static IEnumerable<string> SupportedDistanceMeasureKeys
        {
            get { return distanceMeasures.GetReadOnlyKeys(); }
        }

        private static readonly InternalMeasureDictionary<IDistanceMeasure> distanceMeasures =
            new InternalMeasureDictionary<IDistanceMeasure>(
                new KeyValuePair<string, IDistanceMeasure>(MeasureKeys.DistanceMetre, DistanceMeasures.Metre),
                new KeyValuePair<string, IDistanceMeasure>(MeasureKeys.DistanceDecimetre, DistanceMeasures.Decimetre),
                new KeyValuePair<string, IDistanceMeasure>(MeasureKeys.DistanceCentimetre, DistanceMeasures.Centimetre),
                new KeyValuePair<string, IDistanceMeasure>(MeasureKeys.DistanceMillimetre, DistanceMeasures.Millimetre),
                new KeyValuePair<string, IDistanceMeasure>(MeasureKeys.DistanceInch, DistanceMeasures.Inch));

        #endregion

        #region Time Measure Declarations

        public static readonly ITimeMeasure DefaultTimeMeasure =
            TimeMeasures.Millisecond;

        public static ITimeMeasure GetTimeMeasure(string measureKey)
        {
            if (measureKey == null)
            {
                throw new ArgumentNullException("measureKey");
            }
            Contract.EndContractBlock();
            return timeMeasures.GetValueOrDefault(measureKey);
        }

        public static string GetTimeMeasureKey(ITimeMeasure measure)
        {
            if (measure == null)
            {
                throw new ArgumentNullException("measure");
            }
            Contract.EndContractBlock();
            return timeMeasures.GetKeyOrDefault(measure);
        }

        public static IEnumerable<ITimeMeasure> SupportedTimeMeasures
        {
            get { return timeMeasures.GetReadOnlyValues(); }
        }

        public static IEnumerable<string> SupportedTimeMeasureKeys
        {
            get { return timeMeasures.GetReadOnlyKeys(); }
        }

        private static readonly InternalMeasureDictionary<ITimeMeasure> timeMeasures =
            new InternalMeasureDictionary<ITimeMeasure>(
                new KeyValuePair<string, ITimeMeasure>(MeasureKeys.TimeHour, TimeMeasures.Hour),
                new KeyValuePair<string, ITimeMeasure>(MeasureKeys.TimeMinute, TimeMeasures.Minute),
                new KeyValuePair<string, ITimeMeasure>(MeasureKeys.TimeSecond, TimeMeasures.Second),
                new KeyValuePair<string, ITimeMeasure>(MeasureKeys.TimeMillisecond, TimeMeasures.Millisecond));

        #endregion

        #region Angle Measure Declarations

        public static readonly IAngleMeasure DefaultAngleMeasure =
            AngleMeasures.Degree;

        public static IAngleMeasure GetAngleMeasure(string measureKey)
        {
            if (measureKey == null)
            {
                throw new ArgumentNullException("measureKey");
            }
            Contract.EndContractBlock();
            return angleMeasures.GetValueOrDefault(measureKey);
        }

        public static string GetAngleMeasureKey(IAngleMeasure measure)
        {
            if (measure == null)
            {
                throw new ArgumentNullException("measure");
            }
            Contract.EndContractBlock();
            return angleMeasures.GetKeyOrDefault(measure);
        }

        public static IEnumerable<IAngleMeasure> SupportedAngleMeasures
        {
            get { return angleMeasures.GetReadOnlyValues(); }
        }

        public static IEnumerable<string> SupportedAngleMeasureKeys
        {
            get { return angleMeasures.GetReadOnlyKeys(); }
        }

        private static readonly InternalMeasureDictionary<IAngleMeasure> angleMeasures =
            new InternalMeasureDictionary<IAngleMeasure>(
                new KeyValuePair<string, IAngleMeasure>(MeasureKeys.AngleRadian, AngleMeasures.Radian),
                new KeyValuePair<string, IAngleMeasure>(MeasureKeys.AngleDegree, AngleMeasures.Degree));

        private static readonly IEnumerable<string> angleMeasureKeys =
            new string[]
            {
                GetAngleMeasureKey(AngleMeasures.Radian), 
                GetAngleMeasureKey(AngleMeasures.Degree)
            };

        private static readonly IDictionary<string, string> angleTextFormats =
            angleMeasureKeys
            .Zip(new string[] {
                "F1",
                "F0" },
                (m, f) => new { Measure = m, TextFormat = f })
            .ToDictionary(x => x.Measure, v => v.TextFormat);

        private static readonly IDictionary<string, string> angleMeasureFormatStrings =
            angleMeasureKeys
            .Zip(new string[] {
                " {0}",
                "{0}" },
                (m, f) => new { Measure = m, FormatString = f })
            .ToDictionary(x => x.Measure, v => v.FormatString);

        private static string getAngleValue(
            string measureKey,
            IDictionary<string, string> measureDictionary)
        {
            if (measureKey == null)
            {
                throw new ArgumentNullException("measureKey");
            }
            if (measureDictionary == null)
            {
                throw new ArgumentNullException("measureDictionary");
            }
            Contract.EndContractBlock();
            return measureDictionary.ContainsKey(measureKey) ?
                measureDictionary[measureKey] :
                null;
        }

        private static string getAngleValue(
            IAngleMeasure measure,
            IDictionary<string, string> measureDictionary)
        {
            if (measure == null)
            {
                throw new ArgumentNullException("measure");
            }
            if (measureDictionary == null)
            {
                throw new ArgumentNullException("measureDictionary");
            }
            Contract.EndContractBlock();
            string measureKey = GetAngleMeasureKey(measure);
            return measureKey != null ?
                getAngleValue(measureKey, measureDictionary) :
                null;
        }

        #endregion

        #region GetAngleTextFormat Method

        public static string GetAngleTextFormat(string measureKey)
        {
            if (measureKey == null)
            {
                throw new ArgumentNullException("measureKey");
            }
            Contract.EndContractBlock();
            return getAngleValue(measureKey, angleTextFormats);
        }

        public static string GetAngleTextFormat(IAngleMeasure measure)
        {
            if (measure == null)
            {
                throw new ArgumentNullException("measure");
            }
            Contract.EndContractBlock();
            return getAngleValue(measure, angleTextFormats);
        }

        #endregion

        #region GetAngleMeasureFormatString Method

        public static string GetAngleMeasureFormatString(string measureKey)
        {
            if (measureKey == null)
            {
                throw new ArgumentNullException("measureKey");
            }
            Contract.EndContractBlock();
            return getAngleValue(measureKey, angleMeasureFormatStrings);
        }

        public static string GetAngleMeasureFormatString(IAngleMeasure measure)
        {
            if (measure == null)
            {
                throw new ArgumentNullException("measure");
            }
            Contract.EndContractBlock();
            return getAngleValue(measure, angleMeasureFormatStrings);
        }

        #endregion

        #region GetSettingMeasures Method

        public static ISettingMeasures GetSettingMeasures(
            string distanceMeasureKey,
            string timeMeasureKey,
            string angleMeasureKey)
        {
            if (distanceMeasureKey == null)
            {
                throw new ArgumentNullException("distanceMeasureKey");
            }
            if (timeMeasureKey == null)
            {
                throw new ArgumentNullException("timeMeasureKey");
            }
            if (angleMeasureKey == null)
            {
                throw new ArgumentNullException("angleMeasureKey");
            }
            Contract.EndContractBlock();
            SettingMeasures result = new SettingMeasures
            {
                DistanceMeasure = MeasureHelper.GetDistanceMeasure(distanceMeasureKey)
                    ?? MeasureHelper.DefaultDistanceMeasure,
                TimeMeasure = MeasureHelper.GetTimeMeasure(timeMeasureKey)
                    ?? MeasureHelper.DefaultTimeMeasure,
                AngleMeasure = MeasureHelper.GetAngleMeasure(angleMeasureKey)
                    ?? MeasureHelper.DefaultAngleMeasure
            };
            result.AngleTextFormat =
                MeasureHelper.GetAngleTextFormat(result.AngleMeasure);
            result.AngleMeasureFormatString =
                MeasureHelper.GetAngleMeasureFormatString(result.AngleMeasure);
            return result;

        }

        #endregion
    }
}
